#include "GMFReader.h"
#include "ObjMesh.h"
#include "VertexList.h"
#include "TriangleList.h"
#include "semantic.h"
#include "MeshList.h"
#include "Material.h"
#include "StringUtil.h"
#include "GMFMaterialReader.h"

namespace gmf
{

#define	GMF_BYTE				1
#define GMF_UNSIGNED_BYTE		2
#define GMF_SHORT				3
#define GMF_UNSIGNED_SHORT		4
#define GMF_HALF				5
#define GMF_INT					6
#define GMF_UNSIGNED_INT		7
#define GMF_FLOAT				8
#define GMF_DOUBLE				9

#define GMF_FILE				1
#define GMF_NODE				2
#define GMF_MESH				3
#define GMF_BONE				4
#define GMF_VERTEXARRAY			5
#define GMF_INDICEARRAY			6
#define GMF_PROPERTIES			7
#define GMF_ANIMATIONKEYS		8
#define GMF_AABB				9
#define GMF_SURFACE				10

#define GMF_POSITION			1
#define GMF_NORMAL				2
#define GMF_TEXTURE_COORD		3
#define GMF_COLOR				4
#define GMF_TANGENT				5
#define GMF_BINORMAL			6
#define GMF_BONEINDICE			7
#define GMF_BONEWEIGHT			8

#define GMF_POINTS				1
#define GMF_LINE_STRIP			2
#define GMF_LINE_LOOP			3
#define GMF_LINES				4
#define GMF_TRIANGLE_STRIP		5
#define GMF_TRIANGLE_FAN		6
#define GMF_TRIANGLES			7
#define GMF_QUAD_STRIP			8
#define GMF_QUADS				9
#define GMF_POLYGON				10

	GMFReader::GMFReader(tstring filename):BinaryReader(filename),m_NrOfObjects(0),m_pMeshList(new MeshList()),m_pCurrentMaterial(0)
	{
	}

	GMFReader::~GMFReader(void)
	{
		//delete m_pMeshList;
	}

	bool GMFReader::Read()
	{
		if ( Open() ){
			int start = this->ReadLittleInt();
			int nrOfKids = this->ReadLittleInt();
			int blockSize = this->ReadLittleInt();
			int version = this->ReadLittleInt();

			int type = this->ReadLittleInt();
			if ( type == GMF_MESH )
			{
				ReadGMFMesh();
			}else if ( type == GMF_NODE ){
				nrOfKids = this->ReadLittleInt();
				blockSize = this->ReadLittleInt();
				for (int row = 0; row < 4; ++row){
					for (int column = 0; column < 4;++column){
						float cellvalue = this->ReadLittleFloat();
						m_Transform.SetCell(row,column,cellvalue);
					}
					
				}
				for ( int i = 0; i < nrOfKids; ++i )
				{
					int kidType = this->ReadLittleInt();
					switch(kidType)
					{
					case GMF_PROPERTIES : ReadGMFProperties();break;
					case GMF_SURFACE : ReadGMFSurface();break;
					case GMF_MESH: ReadGMFMesh();break;
					case GMF_BONE:ReadGMFBone();break;
					case GMF_ANIMATIONKEYS: ReadGMFAnimationKeys();break;
					}

				}
			}
			this->Close();
			return true;
		}
		return false;
	}

	bool GMFReader::ReadGMFMesh(){
		int nrOfKids = this->ReadLittleInt();
		int blockSize = this->ReadLittleInt();
		if ( blockSize != 0 )
		{
			for (int row = 0; row < 4; ++row){
				for (int column = 0; column < 4;++column){
					float cellvalue = this->ReadLittleFloat();
					m_Transform.SetCell(row,column,cellvalue);
				}
			}
		}

		for (int i=0; i < nrOfKids; ++i){
			int start = ReadLittleInt();
			switch(start){
			case GMF_MESH: ReadGMFMesh();break;
			case GMF_PROPERTIES: ReadGMFProperties();break;
			case GMF_SURFACE: ReadGMFSurface();break;
			case GMF_BONE: ReadGMFBone();break;
			case GMF_ANIMATIONKEYS: ReadGMFAnimationKeys();break;
			}
		}
		return true;
	}

	bool GMFReader::ReadGMFFile()
	{
		return false;
	}

	bool GMFReader::ReadGMFProperties()
	{
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		int nrOfProperties = ReadLittleInt();
		for (int i= 0; i< nrOfProperties;++i){
			tstring key = this->ReadAsciiString();
			tstring value =this->ReadAsciiString();

			if (key == _T("material")){
				
				// read the material
				tstring currentFile = this->m_FileName;
			
				tstring parentDir = StringUtil::GetParentDirectory(currentFile);
				size_t abstractIndex = value.find(L"abstract::");
				if ( abstractIndex != string::npos )
				{
					if ( abstractIndex+10 < value.length() )
						value = value.substr(abstractIndex+10);
				}
				tstring materialFile = parentDir + L"/" + value;
				m_pCurrentMaterial = new Material(value);
				size_t length = materialFile.length();
				size_t matIndex = materialFile.rfind(L".mat");
				
				if ( matIndex == (materialFile.length() - 4))
				{
					GMFMaterialReader matReader(materialFile);
					matReader.Read();
					if ( matReader.HasKey(L"texture0")){
						tstring value = matReader.GetValue(L"texture0");
						value = value.substr(1,value.length()-2);
						size_t abstractIndex = value.find(L"abstract::");
						if ( abstractIndex != string::npos)
							value = value.substr(abstractIndex+10);
						m_pCurrentMaterial->SetDiffuseTexture(parentDir+L"/"+value);
					}
					if ( matReader.HasKey(L"dx10shader")){
						tstring value = matReader.GetValue(L"dx10shader");
						m_pCurrentMaterial->SetEffectFileName(value);

					}
					if(!matReader.HasKey(L"dx10shader")){
						m_pCurrentMaterial->SetEffectFileName(L"NormalMap.fx");
					}
				}else{
					m_pCurrentMaterial->SetEffectFileName(L"PNTa.fx");
					tstring texture = StringUtil::GetLastDirectoryElement(materialFile);
					m_pCurrentMaterial->SetDiffuseTexture(parentDir+L"/"+texture);
				}
			}else if (key == _T("name")){
				if ( m_pCurrentMaterial != 0 ) 
					m_pCurrentMaterial->SetName(value);

			}
		}
		return true;
	}

	bool GMFReader::ReadGMFSurface()
	{
		cout << "Reading surface"<<endl;
		m_pVertexList= new VertexList(2);
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		for (int i = 0; i < nrOfKids; ++i){
			int start = ReadLittleInt();
			switch(start){
				case GMF_PROPERTIES : ReadGMFProperties();break;
				case GMF_VERTEXARRAY: ReadGMFVertexArray();break;
				case GMF_INDICEARRAY: ReadGMFIndexArray();break;
			}
		}
		int posoffset = m_pVertexList->GetOffset(POSITION);
		int normoffset = m_pVertexList->GetOffset(NORMAL);
		int tcoffset = m_pVertexList->GetOffset(TEXCOORD);

		/*
		cout<<endl<<"Position Data"<<endl;
		cout<<"-------------"<<endl;
		DAEFloat3 vertex;
		int nrOfVertices = m_pVertexList->GetSize();
		for (int i = 0; i< nrOfVertices;++i){
			m_pVertexList->GetVertexData(i,posoffset,&vertex);
			cout<< vertex.x <<","<<vertex.y<<","<<vertex.z<<endl;
		}

		cout<<"Normal Data"<<endl;
		cout<<"-------------"<<endl;

		for (int i = 0; i< nrOfVertices;++i){
			m_pVertexList->GetVertexData(i,normoffset,&vertex);
			cout<< vertex.x <<","<<vertex.y<<","<<vertex.z<<endl;
		}
		DAEFloat2 tc;

		cout<<"Texcoord Data"<<endl;
		cout<<"-------------"<<endl;

		for (int i = 0; i< nrOfVertices;++i){
			m_pVertexList->GetVertexData(i,tcoffset,&tc);
			cout<< tc.x <<","<<tc.y<<endl;
		}
		*/
		if ( m_pCurrentMaterial != 0 )
		{
			m_pCurrentMesh = new ObjMesh(m_pVertexList,m_pIndices, m_pCurrentMaterial);
		}else{
			m_pCurrentMesh = new ObjMesh(m_pVertexList,m_pIndices);
		}
		tstringstream name;
		name << _T("object") << m_NrOfObjects;

		m_pCurrentMesh->SetName(name.str());
		
		m_pMeshList->AddMesh(m_pCurrentMesh);
		m_NrOfObjects++;
		m_pCurrentMaterial = 0;
		return true;	
	}

	bool GMFReader::ReadGMFVertexArray(){
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		int nrOfVertices = ReadLittleInt();
		int semantic = ReadLittleInt();
		int dataType = ReadLittleInt();
		int elements = ReadLittleInt();
		switch(semantic){
			case GMF_POSITION: 
				m_pVertexList->AddSemantic(POSITION,elements);
				ReadGMFPositionData(nrOfVertices,elements);
				break;
			case GMF_NORMAL: 
				m_pVertexList->AddSemantic(NORMAL,elements);
				ReadGMFNormalData(nrOfVertices,elements);
				break;
			case GMF_TEXTURE_COORD:
				m_pVertexList->AddSemantic(TEXCOORD,elements);
				ReadGMFTexCoordData(nrOfVertices,elements);
				break;
			case GMF_TANGENT:
				m_pVertexList->AddSemantic(TANGENT,elements);
				ReadGMFSemanticData(TANGENT,dataType, nrOfVertices,elements);
				break;
			case GMF_BINORMAL:
				m_pVertexList->AddSemantic(BINORMAL,elements);
				ReadGMFSemanticData(BINORMAL,dataType, nrOfVertices,elements);
				break;
			case GMF_BONEINDICE:
				SkipGMFSemanticData(BONEWEIGHT,dataType, nrOfVertices,elements);
				break;
			case GMF_BONEWEIGHT:
				SkipGMFSemanticData(BONEINDEX,dataType, nrOfVertices,elements);
				break;

		}
		return true;
	}

	bool GMFReader::ReadGMFIndexArray(){
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		int nrOfIndices = ReadLittleInt();
		int type =ReadLittleInt();
		int datatype= ReadLittleInt();
		
		if (type == GMF_TRIANGLES && datatype== GMF_UNSIGNED_SHORT ){
			int numTriangles = nrOfIndices/3;
			TriangleList* list = new TriangleList(numTriangles);
			for ( int i = 0 ; i< numTriangles; ++i ){
				int index1 = ReadLittleUShort();
				int index2 = ReadLittleUShort();
				int index3 = ReadLittleUShort();
				list->AddIndexedFace(index1,index2,index3);
			}
			m_pIndices = list;
		}
		return true;
	}

	void GMFReader::ReadGMFPositionData(int nrOfVertices, int nrOfElements){
		int offset = m_pVertexList->GetOffset(POSITION);
		DAEFloat3 vertex;
		DAEFloat3 tVertex;
		for (int i = 0; i< nrOfVertices;++i){
			float x = ReadLittleFloat();
			float y = ReadLittleFloat();
			float z = ReadLittleFloat();
			vertex.SetValues(x,y,z);
			m_Transform.TransformVector(tVertex,vertex);
			m_pVertexList->AddVertexData(i,offset,&tVertex);
		}
		
	}

	void GMFReader::ReadGMFNormalData(int nrOfVertices, int nrOfElements)
	{
		int offset = m_pVertexList->GetOffset(NORMAL);
		DAEFloat3 vertex;
		DAEFloat3 tVertex;
		for (int i = 0; i< nrOfVertices;++i){
			float x = ReadLittleFloat();
			float y = ReadLittleFloat();
			float z = ReadLittleFloat();
			vertex.SetValues(x,y,z);
			m_Transform.TransformNormal(tVertex,vertex);
			m_pVertexList->AddVertexData(i,offset,&tVertex);
		}
		
	}

	void GMFReader::ReadGMFTexCoordData(int nrOfVertices, int nrOfElements){
		int offset = m_pVertexList->GetOffset(TEXCOORD);
		DAEFloat2 vertex;
		for (int i = 0; i< nrOfVertices;++i){
			float x = ReadLittleFloat();
			float y = ReadLittleFloat();
			
			vertex.SetValues(x,y);
			m_pVertexList->AddVertexData(i,offset,&vertex);

			m_pVertexList->GetVertexData(i,offset,&vertex);
				float cx = vertex.x;
				float cy = vertex.y;
		}
	}

	void GMFReader::ReadGMFSemanticData(SEMANTIC semantic, int dataType, int nrOfVertices,int nrOfElements){
		int offset = m_pVertexList->GetOffset(semantic);
		if (nrOfElements ==2 )
		{
			DAEFloat2 vertex;
			for (int i = 0; i< nrOfVertices;++i){
				float x = ReadLittleFloat();
				float y = ReadLittleFloat();

				vertex.SetValues(x,y);
				m_pVertexList->AddVertexData(i,offset,&vertex);

				
			}
		}else if (nrOfElements ==3)
		{
			DAEFloat3 vertex;
			for (int i = 0; i< nrOfVertices;++i){
				float x = ReadLittleFloat();
				float y = ReadLittleFloat();
				float z = ReadLittleFloat();	
				vertex.SetValues(x,y,z);
				m_pVertexList->AddVertexData(i,offset,&vertex);
			}
		}
	}

	void GMFReader::SkipGMFSemanticData(SEMANTIC semantic, int dataType, int nrOfVertices,int nrOfElements){
		switch(dataType){
		case GMF_BYTE: 
		case GMF_UNSIGNED_BYTE: this->Skip(nrOfElements*nrOfVertices);break;
		case GMF_SHORT:
		case GMF_HALF:
		case GMF_UNSIGNED_SHORT:this->Skip(2*nrOfElements*nrOfVertices);break;
		case GMF_INT:
		case GMF_FLOAT:
		case GMF_UNSIGNED_INT:this->Skip(4*nrOfElements*nrOfVertices);break;
		case GMF_DOUBLE:this->Skip(8*nrOfElements*nrOfVertices);break;
		}
	}

	bool GMFReader::ReadGMFBone()
	{
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		DAEMatrix boneMatrix = ReadMatrix();
		int boneId = ReadLittleInt();
		for ( int i = 0;i < nrOfKids; ++i){
			int type = ReadLittleInt();
			switch(type){
			case GMF_PROPERTIES:ReadGMFProperties();break;
			case GMF_ANIMATIONKEYS:ReadGMFAnimationKeys();break;
			}
		}
		return true;
	}

	DAEMatrix GMFReader::ReadMatrix(){
		DAEMatrix matrix;
		for (int row = 0; row < 4; ++row){
			for (int column = 0; column < 4;++column){
				float cellvalue = this->ReadLittleFloat();
				matrix.SetCell(row,column,cellvalue);
			}
		}
		return matrix;
	}

	bool GMFReader::ReadGMFAnimationKeys()
	{
		int nrOfKids = ReadLittleInt();
		int blockSize = ReadLittleInt();
		int nrOfFrames = ReadLittleInt();
		for ( int i = 0 ; i< nrOfFrames; ++i )
		{
			DAEMatrix frame = ReadMatrix();
		}
		return true;
	}
}